/**** SpringProxAPI/Dialog
 *
 * NAME
 *   SpringProxAPI :: Dialog
 *
 * DESCRIPTION
 *   Provides dialog between host and reader
 *
 **/

/*

  SpringProx API
  --------------

  Copyright (c) 2000-2008 SpringCard - www.springcard.com

  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
  TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  PARTICULAR PURPOSE.

  History
  -------
  
  JDA 15/02/2003 : created from CSB-3's osi3964.c
  JDA 16/05/2003 : added GetFirmware and GetRc500Id
  JDA 15/09/2003 : added "burst" mode for USB devices
  JDA 07/04/2004 : moved low-layers to related sprox_dlg_xxx files
  JDA 17/06/2004 : support of multiple readers thanks to SPROX_CreateInstance, SPROX_SelectInstance,
                   and SPROX_DestroyInstance
  JDA 24/06/2006 : now really reentrant under new springprox_ex.dll

*/

#include "sprox_api_i.h"

#include <assert.h>

#ifndef UNDER_CE	
#include <time.h>
#endif

//#undef D
//#define D(x) x

#define SPROX_API_CAN_REPEAT

/*
 * Low layer protocol
 * ------------------
 */
void RecvFlush(SPROX_CTX_ST *sprox_ctx)
{
  BYTE    b;
  WORD    retry = 250;

  SPROX_Trace(TRACE_DLG_HI, "<flush>");

  SerialSetTimeouts(sprox_ctx, 0, INTER_BYTE_TMO);
  retry = 250;
  while ((retry--) && (RecvByte(sprox_ctx, &b)));

  SPROX_Trace(TRACE_DLG_HI, "</flush>");
}

/*
 * High layer protocol : send a frame
 * ----------------------------------
 */
SWORD SendFrame(SPROX_CTX_ST *sprox_ctx, const BYTE *buffer, WORD buflen)
{
#ifdef SPROX_API_ONLY_BIN
  /* Binary protocol only */
  return SendFrameBIN(sprox_ctx, buffer, buflen);
#else
#ifdef SPROX_API_ONLY_ASC
  /* ASCII protocol only */
  return SendFrameASC(sprox_ctx, buffer, buflen);
#else
  /* Protocol depends on reader's settings */
  if (sprox_ctx->settings.bin)
  {
    if (sprox_ctx->settings.bin_bus)
    {
      return SendFrameBUS(sprox_ctx, buffer, buflen);
    }
    return SendFrameBIN(sprox_ctx, buffer, buflen);
  } else if (sprox_ctx->settings.ascii)
  {
    return SendFrameASC(sprox_ctx, buffer, buflen);
  }
  return SendFrameOSI(sprox_ctx, buffer, buflen);
#endif
#endif
}

/*
 * High layer protocol : receive a frame
 * -------------------------------------
 */
SWORD RecvFrame(SPROX_CTX_ST *sprox_ctx, BYTE *buffer, WORD *buflen)
{
#ifdef SPROX_API_ONLY_BIN
  /* Binary protocol only */
  return RecvFrameBIN(sprox_ctx, buffer, buflen);
#else
#ifdef SPROX_API_ONLY_ASC
  /* ASCII protocol only */
  return RecvFrameASC(sprox_ctx, buffer, buflen);
#else
  /* Protocol depends on reader's settings */
  if (sprox_ctx->settings.bin)
  {
    if (sprox_ctx->settings.bin_bus)
    {
      return RecvFrameBUS(sprox_ctx, buffer, buflen);
    }
    return RecvFrameBIN(sprox_ctx, buffer, buflen);
  }
  if (sprox_ctx->settings.ascii)
  {
    return RecvFrameASC(sprox_ctx, buffer, buflen);
  }
  return RecvFrameOSI(sprox_ctx, buffer, buflen);
#endif
#endif
}

/*
 * Try to establish dialog with the reader at specified baudrate
 * -------------------------------------------------------------
 */
SWORD SPROX_ReaderConnectAt(SPROX_CTX_ST *sprox_ctx, DWORD baudrate)
{
  SWORD   rc;

  memset(&sprox_ctx->settings, 0, sizeof(sprox_ctx->settings));

  /* Set the default communication settings */
  /* -------------------------------------- */
#ifdef SPROX_API_ONLY_BIN
  SPROX_Trace(TRACE_DLG_HI, "Only Binary protocol compiled");
  sprox_ctx->want_settings.bin = TRUE;
  sprox_ctx->want_settings.ascii = FALSE;
  sprox_ctx->want_settings.osi = FALSE;
  sprox_ctx->want_settings.bin_bus = FALSE;
#endif
#ifdef SPROX_API_ONLY_ASC
  SPROX_Trace(TRACE_DLG_HI, "Only ASCII protocol compiled");
  sprox_ctx->want_settings.ascii = TRUE;
  sprox_ctx->want_settings.bin = FALSE;
  sprox_ctx->want_settings.osi = FALSE;
  sprox_ctx->want_settings.bin_bus = FALSE;
#endif

  /* OSI, ASCII or Binary ? Binary is default */
  if (sprox_ctx->want_settings.ascii)
    sprox_ctx->settings.ascii = TRUE;
  else if (!sprox_ctx->want_settings.osi)
    sprox_ctx->settings.bin = TRUE;
  /* Provide addressing if needed (support of multiple modules on a single RS-485 line) */
  if (sprox_ctx->want_settings.bin_bus)
    sprox_ctx->settings.bin_bus = TRUE;

  /* RS485 hardware must be remembered */
  if (sprox_ctx->want_settings.rs485)
    sprox_ctx->settings.rs485 = TRUE;

  /* Set the specified baudrate */
  /* -------------------------- */
  if (baudrate)
    if (!SerialSetBaudrate(sprox_ctx, baudrate))
      return MI_SER_ACCESS_ERR;

  /* Call GetFirmware to check if the reader answers */
  /* ----------------------------------------------- */
#ifdef SPROX_API_REENTRANT
  rc = SPROXx_ReaderGetFirmware(sprox_ctx, NULL, 0);
#else
  rc = SPROX_ReaderGetFirmware(NULL, 0);  
#endif

  /* New 1.54 */
  if (rc == MI_POLLING)
  {
#ifdef SPROX_API_REENTRANT
    SPROXx_FindWaitCancel(sprox_ctx);
    rc = SPROXx_ReaderGetFirmware(sprox_ctx, NULL, 0);
#else
	  SPROX_FindWaitCancel();
    rc = SPROX_ReaderGetFirmware(NULL, 0);  
#endif
  }


  if (rc != MI_OK)
  {
    SPROX_Trace(TRACE_DLG_HI, "GetFirmware failed");
  } else
  {
    /* Confirm thanks with GetFeatures */
    /* ------------------------------- */
#ifdef SPROX_API_REENTRANT
    rc = SPROXx_ReaderGetFeatures(sprox_ctx, NULL);
#else
    rc = SPROX_ReaderGetFeatures(NULL);
#endif
    
    if (rc != MI_OK)
    {
      SPROX_Trace(TRACE_DLG_HI, "GetFeatures failed");
    }
  }

#if (defined(WINCE) && (defined(SPROX_API_ONLY_BIN) || defined(SPROX_API_ONLY_ASC)))
  if (rc != MI_OK)
  {
    /* Call GetFirmware */
#ifdef SPROX_API_REENTRANT
    rc = SPROXx_ReaderGetFirmware(sprox_ctx, NULL, 0);
#else
    rc = SPROX_ReaderGetFirmware(NULL, 0);  
#endif
    if (rc != MI_OK)
    {
      SPROX_Trace(TRACE_DLG_HI, "GetFirmware failed (second try)");
    } else
    {
      /* Call GetFeatures */
#ifdef SPROX_API_REENTRANT
      rc = SPROXx_ReaderGetFeatures(sprox_ctx, NULL);
#else
      rc = SPROX_ReaderGetFeatures(NULL);
#endif
      if (rc != MI_OK)
      {
        SPROX_Trace(TRACE_DLG_HI, "GetFeatures failed (second try)");
      }

    }
  }
#endif

#ifndef SPROX_API_ONLY_BIN
#ifndef SPROX_API_ONLY_ASC
  if ((rc != MI_OK) && (!sprox_ctx->want_settings.bin)
      && (!sprox_ctx->want_settings.osi) && (!sprox_ctx->want_settings.bin_bus) && (!sprox_ctx->want_settings.ascii))
  {
    /* Activation with protocol different from OSI has failed */
    /* We revert to OSI for legacy support of CSB3 family     */
    /* ------------------------------------------------------ */

    sprox_ctx->settings.bin = FALSE;

    /* Call GetFirmware */
#ifdef SPROX_API_REENTRANT
    rc = SPROXx_ReaderGetFirmware(sprox_ctx, NULL, 0);
#else
    rc = SPROX_ReaderGetFirmware(NULL, 0);  
#endif
    if (rc != MI_OK)
    {
      SPROX_Trace(TRACE_DLG_HI, "GetFirmware failed (second try)");
    } else
    {
      /* Call GetFeatures */
#ifdef SPROX_API_REENTRANT
      rc = SPROXx_ReaderGetFeatures(sprox_ctx, NULL);
#else
      rc = SPROX_ReaderGetFeatures(NULL);
#endif
      if (rc != MI_OK)
      {
        SPROX_Trace(TRACE_DLG_HI, "GetFeatures failed (second try)");
      }

    }
  }
#endif
#endif

  if (rc != MI_OK)
    return rc;

  /* Take decision if current protocol is not the best choice */
  /* -------------------------------------------------------- */
  if (sprox_ctx->sprox_capabilities & SPROX_WITH_BIN_PROTOCOL)
  {
    /* Reader is able to dialog in Binary (faster) */
    if ((!sprox_ctx->want_settings.ascii) && (!sprox_ctx->want_settings.osi))
    {
      /* Go binary ! */
      sprox_ctx->settings.bin = TRUE;
    }
  }

  return MI_OK;
}

/*
 * Try to establish dialog with the reader, trying both baudrates
 * --------------------------------------------------------------
 */
SWORD SPROX_ReaderConnect(SPROX_CTX_ST *sprox_ctx)
{
  BYTE    buffer[20] = { 0 };
  SWORD   rc = MI_SER_ACCESS_ERR;
  BOOL    just_reset = FALSE;
  BYTE    i;
  
  assert(sprox_ctx != NULL);
  
  switch (DefaultForceBaudrate)
  {
    case 38400  : sprox_ctx->want_settings.bdr_38400  = TRUE;
                  sprox_ctx->want_settings.bdr_115200 = FALSE;
                  break;
    case 115200 : sprox_ctx->want_settings.bdr_38400  = FALSE;
                  sprox_ctx->want_settings.bdr_115200 = TRUE;
                  break;
  }
  
  sprox_ctx->com_active = FALSE;

  /* If we actually have resetted the SpringProx, it shoul'd display is ID now */
  /* ------------------------------------------------------------------------- */
  if ((sprox_ctx->com_reset_ctrl) || (sprox_ctx->com_power_ctrl) || (sprox_ctx->com_power_auto))
  {
    /* Reader is supposed to be resetting, we expect a startup string */
    SerialSetBaudrate(sprox_ctx, 38400);

    /* We shall wait up to 2s for reader's reset */
    SerialSetTimeouts(sprox_ctx, 2000, 100);

    SPROX_Trace(TRACE_DLG_HI, "Waiting for reader startup string");
    RecvBurst(sprox_ctx, buffer, sizeof(buffer));

    for (i = 0; i < sizeof(buffer) - 4; i++)
    {
      /* Known reader startup strings */
      if ((!strncmp((char *) &buffer[i], "SPX", 3))  /* SpringProx family */
       || (!strncmp((char *) &buffer[i], "CSB", 3))  /* CSB family */
       || (!strncmp((char *) &buffer[i], "531", 3))  /* MOD K531 family */
       || (!strncmp((char *) &buffer[i], "632", 3))) /* RFU... */
      {
        /* Reader is resetting, don't expect it to be at 38400 */
        just_reset = TRUE;
        SPROX_Trace(TRACE_DLG_HI, "Reader resetting : %c%c%c%c", buffer[i + 0], buffer[i + 1], buffer[i + 2], buffer[i + 3]);
        break;
      }
    }

    if (!just_reset)
    {
      /* Reader has not successfully resetted */
#ifdef UNDER_CE
      /* This shoul'd be considered has a fatal error, since CF or SDIO devices MUST */
      /* do this, but we are optimistic and continue anyway...                       */
#endif
    }

    /* Clear reader status */
    memset(&sprox_ctx->settings, 0, sizeof(sprox_ctx->settings));
  }
#ifdef SPROX_HIGH_BAUDRATE
  /* Maybe is the reader already at 115200bps ? */
  /* ------------------------------------------ */
  if ((!just_reset) && (!sprox_ctx->want_settings.bdr_38400))
  {
    rc = SPROX_ReaderConnectAt(sprox_ctx, 115200);
    if (rc == MI_OK)
    {
      SPROX_Trace(TRACE_DLG_HI, "Reader found at 115200bps");
      sprox_ctx->settings.bdr_115200 = TRUE;
      sprox_ctx->com_active = TRUE;
      return MI_OK;
    } else
    {
      SPROX_Trace(TRACE_DLG_HI, "Connect at 115200bps failed, rc=%d", rc);
    }
  }
#endif

  /* Try to connect to the reader, at 38400bps */
  /* ----------------------------------------- */
  if (!sprox_ctx->want_settings.bdr_115200)
  {
    rc = SPROX_ReaderConnectAt(sprox_ctx, 38400);
    if (rc == MI_OK)
    {
      /* Connected */
#ifdef SPROX_HIGH_BAUDRATE
      if ((sprox_ctx->sprox_capabilities & SPROX_WITH_BAUDRATE_115200) && (!sprox_ctx->want_settings.bdr_38400))
      {
        /* Going to 115200bps */
        BYTE    buffer[2];

        SPROX_Trace(TRACE_DLG_HI, "Reader found at 38400bps, going to 115200bps");

        buffer[0] = SPROX_CONTROL_BAUDRATE;
        buffer[1] = 115;
#ifdef SPROX_API_REENTRANT
        rc = SPROXx_Function(sprox_ctx, SPROX_CONTROL, buffer, sizeof(buffer), NULL, NULL);
#else
        rc = SPROX_Function(SPROX_CONTROL, buffer, sizeof(buffer), NULL, NULL);
#endif
        if (rc == MI_UNKNOWN_FUNCTION)
        {
          SPROX_Trace(TRACE_DLG_HI, "Reader doesn't allow 115200bps");
          sprox_ctx->com_active = TRUE;
          return MI_OK;
        }
        if ((rc == MI_OK) || (rc == MI_SER_CHECKSUM_ERR) || (rc == MI_SER_PROTO_ERR) || (rc == MI_SER_TIMEOUT_ERR))
        {
          /* Switch our UART to high-speed also */
          SPROX_Trace(TRACE_DLG_HI, "Reader accepted 115200");

          /* Try to establish contact again... (3 tries) */
          for (i = 0; i < 3; i++)
          {
            /* Give reader at least 25ms for UART reset */
            Sleep(25);

            rc = SPROX_ReaderConnectAt(sprox_ctx, 115200);
            if (rc == MI_OK)
            {
              SPROX_Trace(TRACE_DLG_HI, "Reader found at 115200bps");
              sprox_ctx->settings.bdr_115200 = TRUE;
              sprox_ctx->com_active = TRUE;
              return MI_OK;
            }
            Sleep(175);
          }

          SPROX_Trace(TRACE_DLG_HI, "Reader lost at 115200bps");
          return rc;
        } else
        {
          SPROX_Trace(TRACE_DLG_HI, "Reader said : rc=%d", rc);
        }
      } else
#endif
      {
        /* We must stay at 38400 */
        SPROX_Trace(TRACE_DLG_HI, "Reader found at 38400bps");
        sprox_ctx->com_active = TRUE;
        return MI_OK;
      }
    } else
    {
      SPROX_Trace(TRACE_DLG_HI, "Connect at 38400bps failed, rc=%d", rc);
    }
  }
#ifdef UNDER_CE
#ifdef SPROX_HIGH_BAUDRATE
  /* Maybe is the reader already at 115200bps ? */
  if (!sprox_ctx->want_settings.bdr_38400)
  {
    rc = SPROX_ReaderConnectAt(sprox_ctx, 115200);
    if (rc == MI_OK)
    {
      SPROX_Trace(TRACE_DLG_HI, "Reader found at 115200bps");
      sprox_ctx->com_active = TRUE;
      return MI_OK;
    } else if (rc == MI_UNKNOWN_FUNCTION)
    {
      sprox_ctx->com_active = TRUE;
      return MI_OK;
    } else
    {
      SPROX_Trace(TRACE_DLG_HI, "Connect at 115200bps failed, rc=%d", rc);
    }
  }
#endif
#endif

  return rc;
}

/*
 * High layer protocol : half-duplex frame exchange
 * -------------------------------------------------
 */
static SWORD SPROX_Function_Send(SPROX_CTX_ST *sprox_ctx, BYTE command, const BYTE *send_data, WORD send_len)
{
  BYTE buffer[SPROX_FRAME_CONTENT_SIZE+20];
  WORD pos, len, i;
  BYTE crc;
  SWORD rc;
  
 
  assert(sprox_ctx != NULL);
  
  /* Check parameters... */
  if (send_len >= SPROX_FRAME_CONTENT_SIZE)
  {
    SPROX_Trace(TRACE_DLG_HI, "MI_COMMAND_OVERFLOW (%d>=%d)", send_len, SPROX_FRAME_CONTENT_SIZE);
    return MI_COMMAND_OVERFLOW;
  }
  
  if (send_data == NULL) send_len = 0;
  
  /* Create frame header */
  pos = 0;
  buffer[pos++] = sprox_ctx->com_sequence;
  buffer[pos++] = command;
  len = send_len;
  while (len >= 0x80)
  {
    buffer[pos++] = 0x80;
    len -= 0x80;
  }
  buffer[pos++] = (BYTE) len;

  /* Add frame data */
  if (send_data != NULL)
  {
    for (i=0; i<send_len; i++)
      buffer[pos++] = send_data[i];
  }

  /* Append CRC at the end of the frame */
  crc = 0;
  for (i=0; i<pos; i++)
    crc ^= buffer[i];
  buffer[pos++] = crc;

  /* Send the frame */
  rc = SendFrame(sprox_ctx, buffer, pos);
  if (rc != MI_OK)
  {
    SPROX_Trace(TRACE_DLG_HI, "Send %02X failed : %d", command, rc);
    return rc;
  }

  return MI_OK;
}

static SWORD SPROX_Function_Recv(SPROX_CTX_ST *sprox_ctx, BYTE *sequence, BYTE *recv_data, WORD *recv_len)
{
  BYTE buffer[SPROX_FRAME_CONTENT_SIZE+20];
  WORD pos, len, i;
  BYTE crc;  
  SWORD rc;
  
   
  assert(sprox_ctx != NULL);
    
time_extension:

  pos = sizeof(buffer);
  rc = RecvFrame(sprox_ctx, buffer, &pos);
  if (rc != MI_OK)
  {
    if (rc == MI_TIME_EXTENSION)
    {
      SPROX_Trace(TRACE_DLG_HI, "<TIME_EXTENSION>");
      goto time_extension;
    }
    SPROX_Trace(TRACE_DLG_HI, "Recv failed : %d", rc);
    return rc;
  }

  /* Check the CRC in the answer */
  if (!sprox_ctx->settings.ascii)
  {
    /* No CRC in ASCII protocol */
    crc = 0;
    for (i=0; i<pos; i++)
      crc ^= buffer[i];

    if (crc)
    {
      /* CRC is KO */
      SPROX_Trace(TRACE_DLG_HI, "MI_SER_CHECKSUM_ERR");
      return MI_SER_CHECKSUM_ERR;
    }
  }

  /* Decode received header */
  pos = 0;
  *sequence = buffer[pos++];
  rc = 0 - buffer[pos++];
  len = 0;
  while (buffer[pos] >= 0x80)
    len += buffer[pos++];
  len += buffer[pos++];

  /* Time extension ? (new 1.20) */
  if (buffer[1] == (0 - MI_TIME_EXTENSION))
  {
    SPROX_Trace(TRACE_DLG_HI, "<TIME_EXTENSION>");
    goto time_extension;
  }
    
  /* Decode received data */
  if ((recv_data != NULL) && (recv_len != NULL))
  {
    if (len > *recv_len)
    {
      SPROX_Trace(TRACE_DLG_HI, "MI_RESPONSE_OVERFLOW (%d>%d)", len, *recv_len);
      return MI_RESPONSE_OVERFLOW;
    }

    for (i = 0; i < len; i++)
      recv_data[i] = buffer[pos++];

    *recv_len = len;
  }

  return rc;
}


SPROX_API_FUNC(Function) (SPROX_PARAM  BYTE command, const BYTE *send_data, WORD send_len, BYTE *recv_data, WORD *recv_len)
{
  SWORD rc, first_rc = MI_OK;
  BYTE  recv_sequence;
  BYTE  retry = 3;
  WORD  first_recv_len = 0;

  SPROX_PARAM_TO_CTX; 
  
#ifdef SPROX_API_WITH_BRCD
  if (sprox_ctx->settings.brcd)
  {
  	return SPROX_API_CALL(Brcd_Function) (SPROX_PARAM_P  command, send_data, send_len, recv_data, recv_len);
  }
#endif
  
  if (recv_len != NULL) first_recv_len = *recv_len;
  
  /* Send the command */
send_command:
  rc = SPROX_Function_Send(sprox_ctx, command, send_data, send_len);
  if (rc != MI_OK) return rc;
 
  /* Retrieve the answer */
recv_answer:
  rc = SPROX_Function_Recv(sprox_ctx, &recv_sequence, recv_data, recv_len);
 
  if (rc != MI_OK)
  {
    /* Error in receive function */   
    if (first_rc == MI_OK)
      first_rc = rc; /* Remember first error code */

    if (sprox_ctx->com_active && retry)
    {
      retry--;
      
      if ((rc == MI_SER_CHECKSUM_ERR)
       || (rc == MI_SER_PROTO_ERR)
       || (rc == MI_SER_TIMEOUT_ERR)
       || (rc == MI_SER_NORESP_ERR))
      {
        /* This is an error due to communication layer, we ask the reader to repeat its last frame  */
        /* REPEAT_PLEASE command is only implemented in readers >= 1.40 with separate RX/TX buffer, */
        /* so most of the time we will receive a MI_UNKNOWN_FUNCTION error                          */
        if (sprox_ctx->sprox_capabilities & SPROX_WITH_DUAL_BUFFERS)
        {
	        rc = SPROX_Function_Send(sprox_ctx, SPROX_REPEAT_PLEASE, NULL, 0);
	        if (rc != MI_OK) return first_rc;
          if (recv_len != NULL) *recv_len = first_recv_len;
          goto recv_answer;
        }
      }
      if (rc == MI_SER_PROTO_NAK)
      {
        /* Reader says NACK, we send our last frame once again with a lot of hope... */
        if (recv_len != NULL) *recv_len = first_recv_len;
        goto send_command;
      } 
    }

    /* Restore first error code */
    rc = first_rc;
  }
  
  /* No sequence in ASCII protocol */
  if (!sprox_ctx->settings.ascii)
  {
    if (recv_sequence != sprox_ctx->com_sequence)
    {
      /* Wrong sequence number */
      SPROX_Trace(TRACE_DLG_HI, "SEQ %02X != %02X", sprox_ctx->com_sequence, recv_sequence);
      if (sprox_ctx->com_active && retry)
      {
        /* If we just sent the REPEAT_PLEASE command and we receive a wrong sequence number, it is    */
        /* likely that reader has totally lost our last frame. We send it again with a lot of hope... */
        retry--;
        if (recv_len != NULL) *recv_len = first_recv_len;
        goto send_command;
      }      
      return MI_SER_PROTO_ERR;
    }
  }

  /* Next sequence */
  sprox_ctx->com_sequence++;

  /* Return the status code */
  return rc;
}

/* New 1.54 */
SPROX_API_FUNC(FunctionWaitResp) (SPROX_PARAM  BYTE *recv_data, WORD *recv_len, WORD timeout_s)
{	
	SWORD rc;
	BYTE recv_sequence;
	time_t wait_until = 0;
	SPROX_PARAM_TO_CTX;

	if (timeout_s != 0xFFFF)
	{
#ifndef UNDER_CE	
		wait_until  = time(NULL);
		wait_until += timeout_s + 1;
#else
	  return MI_LIB_CALL_ERROR;
#endif
	}
	
	for (;;)
	{	
    rc = SPROX_Function_Recv(sprox_ctx, &recv_sequence, recv_data, recv_len);
 
    if (rc != MI_SER_NORESP_ERR)
      break;
      
#ifndef UNDER_CE	
    if ((wait_until != 0) && (wait_until < time(NULL)))
      break;
#endif
  }
  
  return rc;
}

/**f* SpringProxAPI/SPROX_ReaderOpen
 *
 * NAME
 *   SPROX_ReaderOpen
 *
 * DESCRIPTION
 *   Open the SpringProx reader
 *
 * INPUTS
 *   TCHAR device[]     : name of the device where is SpringProx reader is to be found
 *
 * RETURNS
 *   MI_OK              : success
 *   MI_SER_ACCESS_ERR  : reader not found
 *   Other code if internal or communication error has occured.
 *
 * NOTES
 *   For Desktop PC, device can be "COM1" to "COM9" or "USB". If device is set to NULL,
 *   an automatic detection of the reader is performed. On success, the selected device
 *   can be retrieved using SPROX_GetReaderDevice.
 *   New version 1.40 : support of multiple USB devices is now possible, using names
 *   "USB1" to "USB9". Earlier name "USB" remains as an alias of "USB1". Access to
 *   USB device through serial number is also possible with "USB:xxxxx" where xxxxx is
 *   the serial number. Use SPROX_EnumUSBDevices to build the list of available
 *   devices.
 *
 *   For Pocket PC, device must remain NULL. The CF module is implicitly powered up.
 **/

SPROX_API_FUNC(ReaderOpen) (SPROX_PARAM  const TCHAR device[])
{
  SPROX_PARAM_TO_CTX;

  if (device != NULL)
  {
  	SPROX_Trace(TRACE_ACCESS, "ReaderOpen(%s)", device);
  	
#ifdef SPROX_API_WITH_BRCD
    if (!_tcsncicmp(device, _T("BARACODA"), 8))
    {
    	if (!_tcsncicmp(&device[8], _T("_DIRECT:"), 8))
    		return SPROX_API_CALL(Brcd_ReaderOpen) (SPROX_PARAM_P  &device[16], TRUE);

  		return SPROX_API_CALL(Brcd_ReaderOpen) (SPROX_PARAM_P  &device[9], FALSE);
    }
#endif
  	
  } else
  {
    SPROX_Trace(TRACE_ACCESS, "ReaderOpen(NULL)");
  }

  if (sprox_ctx->sprox_status >= 2)
    return MI_OK;

  if (sprox_ctx->sprox_status >= 1)
  {
    SerialClose(sprox_ctx);
    sprox_ctx->sprox_status = 0;
  }

  memset(&sprox_ctx->settings, 0, sizeof(sprox_ctx->settings));

  if ((device != NULL) && (_tcslen(device)))
  {
  	/* A device name has been specified */
  	if (_tcsicmp(device, _T("auto")))
	  {
	  	/* Device name is different from "auto", let's try to open specified device */
	    if (!SerialOpen(sprox_ctx, device))
	    {
	      SPROX_Trace(TRACE_ACCESS, "SPROX_ReaderOpen(%s) ERROR", device);
	      return MI_SER_ACCESS_ERR;
	    }
	    /* Try to connect to the specified device */
	    if (SPROX_ReaderConnect(sprox_ctx) != MI_OK)
	    {
	      SerialClose(sprox_ctx);
	      SPROX_Trace(TRACE_ACCESS, "SPROX_ReaderConnect(%s) ERROR", device);
	      return MI_SER_NORESP_ERR;
	    }

	    goto success;
	  } else
	  {
	  	/* Device name is "auto", let's force a lookup */
		  if (!SerialLookup(sprox_ctx))
		    return MI_SER_ACCESS_ERR;
	  	
	  	goto success;
	  }
  }
  
  if (GetDefaultDevice(sprox_ctx))
  {
  	/* A device name is specified in configuration area */
#ifdef WIN32    
#ifdef UNDER_CE
    /* Two tries may required on SDIO to recover from some errors */
    if (sprox_ctx->com_type & DEVICE_IS_SDIO)
    {
      SPROX_Trace(TRACE_ACCESS, "SDIO device");
	    /* SDIO device doesn't work at 115200bps */
      sprox_ctx->want_settings.bdr_38400 = TRUE;
    	
      if (SerialOpen(sprox_ctx, NULL))
        if (SPROX_ReaderConnect(sprox_ctx) == MI_OK)
          goto success;
    }
#endif
#endif
    if (!SerialOpen(sprox_ctx, NULL))
    {
      SPROX_Trace(TRACE_ACCESS, "SPROX_ReaderOpen(%s) ERROR", device);
      return MI_SER_ACCESS_ERR;
    }
    /* Try to connect to the specified device */
    if (SPROX_ReaderConnect(sprox_ctx) != MI_OK)
    {
      SerialClose(sprox_ctx);
      SPROX_Trace(TRACE_ACCESS, "SPROX_ReaderConnect(%s) ERROR", device);
      return MI_SER_NORESP_ERR;
    }

    goto success;
  } else
  {  
#ifdef WIN32
#ifdef UNDER_CE
	  /* On PocketPC we MUST find the name in registry since it comes from active drivers */
    return MI_SER_ACCESS_ERR;
#endif
#endif
  }

  /* Device name not specified in configuration, at last perform discovery */
  if (!SerialLookup(sprox_ctx))
    return MI_SER_ACCESS_ERR;

success:
  sprox_ctx->sprox_status = 2;
  SPROX_Trace(TRACE_ACCESS, "SPROX_ReaderOpen OK");
  return MI_OK;
}

#ifdef WIN32
SPROX_API_FUNC(ReaderOpenW) (SPROX_PARAM  const wchar_t *device)
{
#ifdef UNICODE
  return SPROX_API_CALL(ReaderOpen) (SPROX_PARAM_P  device);
#else
  char buffer[64+1];
  int i;
  if (device == NULL) return SPROX_API_CALL(ReaderOpen) (SPROX_PARAM_P  NULL);
  for (i=0; i<64; i++)
  {
    buffer[i] = (char) device[i];
    if (buffer[i] == '\0') break;
  }
  buffer[64] = '\0';
  return SPROX_API_CALL(ReaderOpen) (SPROX_PARAM_P  buffer);
#endif
}
#ifndef UNDER_CE
SPROX_API_FUNC(ReaderOpenA) (SPROX_PARAM  const char *device)
{
#ifndef UNICODE
  return SPROX_API_CALL(ReaderOpen) (SPROX_PARAM_P  device);
#else
  wchar_t buffer[64+1];
  int i;
  if (device == NULL) return SPROX_API_CALL(ReaderOpen) (SPROX_PARAM_P  NULL);
  for (i=0; i<64; i++)
  {
    buffer[i] = device[i];
    if (buffer[i] == '\0') break;
  }
  buffer[64] = '\0';
  return SPROX_API_CALL(ReaderOpen) (SPROX_PARAM_P  buffer);
#endif
}
#endif
#endif

/**f* SpringProxAPI/SPROX_ReaderClose
 *
 * NAME
 *   SPROX_ReaderClose
 *
 * DESCRIPTION
 *   Close the SpringProx reader
 *
 * RETURNS
 *   MI_OK              : success 7
 *
 * NOTES 
 *   The RF field is halted before.
 *   On Pocket PC, the CF module is implicitly powered down.
 **/

SPROX_API_FUNC(ReaderClose) (SPROX_PARAM_V)
{
  SPROX_PARAM_TO_CTX;

  SPROX_Trace(TRACE_ACCESS, "SPROX_ReaderClose");
  if (sprox_ctx->sprox_status >= 1)
  {
    SerialClose(sprox_ctx);
    sprox_ctx->sprox_status = 0;
  }
  return MI_OK;
}

/**f* SpringProxAPI/SPROX_ReaderActivate
 *
 * NAME
 *   SPROX_ReaderActivate
 *
 * DESCRIPTION
 *   Powers up the SpringProx reader again after a successfull call to SPROX_ReaderDeactivate
 *
 * RETURNS
 *   MI_OK              : success
 *   MI_SER_ACCESS_ERR  : reader not found
 *   Other code if internal or communication error has occured.
 *
 * NOTES
 *   If this fails, call immediatly SPROX_ReaderOpen(NULL) to try again
 *
 **/

SPROX_API_FUNC(ReaderActivate) (SPROX_PARAM_V)
{  
  SWORD rc;
  SPROX_PARAM_TO_CTX;

#ifdef SPROX_API_WITH_BRCD
  if (sprox_ctx->settings.brcd)
  	sprox_ctx->sprox_status = 2;
#endif

  if (sprox_ctx->sprox_status >= 2)
    return MI_OK;

  if (sprox_ctx->sprox_status < 1)
    return MI_SER_ACCESS_ERR;

  if (sprox_ctx->com_power_auto)
  {
    /* Power up and connect again... */
    if (!SerialOpen(sprox_ctx, sprox_ctx->com_name))
    {
      return MI_SER_ACCESS_ERR;
    }
  } else
  if (sprox_ctx->com_power_ctrl)
  {
    /* Power up and connect again... */
    if (!SerialPowerUp(sprox_ctx))
    {
      SerialClose(sprox_ctx);
      sprox_ctx->sprox_status = 0;
      return MI_SER_ACCESS_ERR;
    }
  }

  /* After power up, reconnect the reader */
  if ((sprox_ctx->com_power_ctrl) || (sprox_ctx->com_power_auto))
  {
    if (SPROX_ReaderConnect(sprox_ctx) != MI_OK)
    {
      SPROX_Trace(TRACE_ACCESS, "SPROX_ReaderConnect ERROR");
      SerialClose(sprox_ctx);
      sprox_ctx->sprox_status = 0;
      return MI_SER_ACCESS_ERR;
    }
    goto ready;
  }

  /* Only enable RF field again */
  rc = SPROX_API_CALL(ControlRF) (SPROX_PARAM_P  TRUE);
  if ((rc != MI_OK) && (rc != MI_UNKNOWN_FUNCTION))
  {
    SPROX_Trace(TRACE_ACCESS, "SPROX_ReaderControlRF ERROR");
    SerialClose(sprox_ctx);
    sprox_ctx->sprox_status = 0;
    return MI_SER_ACCESS_ERR;
  }

ready:
  SPROX_Trace(TRACE_ACCESS, "SPROX_ReaderActivate OK");
  sprox_ctx->sprox_status = 2;
  return MI_OK;
}

/**f* SpringProxAPI/SPROX_ReaderDeactivate
 *
 * NAME
 *   SPROX_ReaderDeactivate
 *
 * DESCRIPTION
 *   Powers down the SpringProx reader without closing the serial comm device
 *
 * RETURNS
 *   MI_OK              : success
 *   Other code if internal or communication error has occured.
 *
 * NOTES
 *   This function is highly dependend of the hardware platform. On Pocket PC
 *   devices it actually powers down the reader in order to reduce power consumption.
 *   On Desktop PC it does nothing.
 **/

SPROX_API_FUNC(ReaderDeactivate) (SPROX_PARAM_V)
{
  SPROX_PARAM_TO_CTX;

  SPROX_Trace(TRACE_ACCESS, "SPROX_ReaderDeactivate");

  if (sprox_ctx->sprox_status < 1)
    return MI_SER_ACCESS_ERR;
  if (sprox_ctx->sprox_status < 2)
    return MI_OK;

  sprox_ctx->sprox_status = 1;

#ifdef SPROX_API_WITH_BRCD
  if (sprox_ctx->settings.brcd)
  	return MI_OK;
#endif

  if (sprox_ctx->com_power_auto)
  {
    /* Closing is enough... */
    SerialClose(sprox_ctx);
    return MI_OK;
  }

  if (sprox_ctx->com_power_ctrl)
  {
    /* Powering down is enough... */
    SerialPowerDown(sprox_ctx);
    return MI_OK;
  }

  /* Turn OFF RF field only */
  SPROX_API_CALL(ControlRF) (SPROX_PARAM_P  FALSE);
  return MI_OK;
}
